/**
    MVP Tree data structure using graph database for I/O storage
    Copyright (C) 2014  David G. Starkweather

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    David Starkweather starkd88@gmail.com
**/

package org.phash.mvptree;

import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Transaction;


/**
 * Datapoint class for representing unit of
 * data in mvptree as a byte[3][5] datatype.
 **/
public class CMDatapoint extends Datapoint {


    public CMDatapoint(Node node){

	super(node);

    }


    public CMDatapoint(Node node, float[][] data){

	super(node);
	setData(data);

    }

    public CMDatapoint(Datapoint point){
	
	super(point);

    }


    public void setData(float[][] data){
	if (data == null || data[0] == null){
	    throw new BadDataException();
	}

	// reshape data
	int m = data.length;
	int n = data[0].length;
	float[] flatdata = new float[m*n];
	int index = 0;
	for (int i=0;i<m;i++){
	    for (int j=0;j<n;j++){
		flatdata[index++]= data[i][j];
	    }
	}

	Node node = getNode();
	Transaction tx = node.getGraphDatabase().beginTx();
	try {
	    node.setProperty(DATA, flatdata);
	    tx.success();
	} finally {
	    tx.finish();
	}
    }

    public float[][] getData(){
	Node node = getNode();
	Transaction tx = node.getGraphDatabase().beginTx();
	float[][] data= null;
	float[] flatdata = null;
	try {
	    flatdata = (float[])getNode().getProperty(DATA, 0);
	    tx.success();
	} finally {
	    tx.finish();
	}

	if (flatdata != null){
	    data = new float[3][flatdata.length/3];
	    int index=0;
	    for (int i=0;i<data.length;i++){
		for (int j=0;j<data[i].length;j++){
		    data[i][j] = flatdata[index++];
		}
	    }
	}
	return data;
    }

    /**
     * compute L1 distance betweeen two points 
     *
     * @param point other Datapoint with which to compare
     * @return float val for distance between two points 
     *
     **/
    public float distance(Datapoint point){
	Datapoint.incrementNumOps();
	CMDatapoint pnt = (CMDatapoint)point;
	float[][] x = this.getData();
	float[][] y = pnt.getData();

	if (x == null || y == null || x.length != y.length || x[0].length != y[0].length)
	    throw new BadDistanceValueException("bad distance value");

	float norm = x.length*x[0].length;
	float sum = 0.0f;
	for (int i=0;i<x.length;i++){
	    for (int j=0;j<x[i].length;j++){
		sum += Math.abs(((float)x[i][j] - (float)y[i][j]));
	    }
	}

	return sum;
    }

}